package edu.uoc.vmerinom.tfc.action;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.faces.model.SelectItem;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;

import edu.uoc.vmerinom.tfc.domain.Casilla;
import edu.uoc.vmerinom.tfc.domain.Grupo;
import edu.uoc.vmerinom.tfc.domain.Hora;
import edu.uoc.vmerinom.tfc.domain.Usuario;
import edu.uoc.vmerinom.tfc.service.HorarioService;
import edu.uoc.vmerinom.tfc.support.Constants;
import edu.uoc.vmerinom.tfc.support.Message;
import edu.uoc.vmerinom.tfc.support.Utils;

/**
 * Manejador que contiene las acciones relacionadas con la gestión del horario.
 * 
 * @author Víctor Manuel Merino Martínez
 * 
 */
@Name("horarioAction")
@Scope(ScopeType.CONVERSATION)
public class HorarioAction {
	/**
	 * Lista con las horas y los grupos asociados a esas horas.
	 */
	List<Map<String, Object>> horario;

	/**
	 * Servicio de horas.
	 */
	@In(create = true)
	HorarioService horarioService;

	/**
	 * Usuario autenticado.
	 */
	@In
	Usuario usuario;

	/**
	 * Hora seleccionada del reloj.
	 */
	String horaReloj;

	/**
	 * Minuto seleccionado del reloj.
	 */
	String minutoReloj;

	/**
	 * Listado de horas;
	 */
	List<SelectItem> horas;

	/**
	 * Listado de minutos.
	 */
	List<SelectItem> minutos;

	/**
	 * Casillas.
	 */
	List<Casilla> casillas;

	/**
	 * Rango de horas.
	 */
	String rango;

	/**
	 * Hora seleccionada.
	 */
	Hora hora;

	/**
	 * Grupo seleccionado.
	 */
	Grupo grupo;

	/**
	 * Listado de grupos.
	 */
	List<Grupo> grupos;

	/**
	 * Casilla seleccionada.
	 */
	Casilla casilla;

	/**
	 * Se mostrará el panel de edición de hora.
	 */
	boolean editHora;

	/**
	 * Se mostrará el panel de edición de casilla.
	 */
	boolean editCasilla;

	/**
	 * Carga los datos iniciales para mostrar la primera pantalla.
	 */
	public void load() {
		if (this.horas == null || this.horas.isEmpty()) {
			this.horas = Utils.getHoras();
		}
		if (minutos == null || minutos.isEmpty()) {
			minutos = Utils.getMinutos();
		}
		List<Hora> horas = null;
		if (usuario.getPerfil().isAdministrador()) {
			horas = horarioService.obtenerHoras();
		} else {
			horas = horarioService.obtenerHorasActivas();
		}
		casillas = horarioService.obtenerCasillas(horas);
		Map<String, Object> fila;
		if (horas != null && !horas.isEmpty()) {
			horario = new ArrayList<Map<String, Object>>();
			for (Hora hora : horas) {
				fila = new HashMap<String, Object>();
				fila.put(Constants.MAP_ATT_HORA, hora);
				fila.put(Constants.MAP_ATT_LUNES, obtenerCasilla(hora, 1));
				fila.put(Constants.MAP_ATT_MARTES, obtenerCasilla(hora, 2));
				fila.put(Constants.MAP_ATT_MIERCOLES, obtenerCasilla(hora, 3));
				fila.put(Constants.MAP_ATT_JUEVES, obtenerCasilla(hora, 4));
				fila.put(Constants.MAP_ATT_VIERNES, obtenerCasilla(hora, 5));
				fila.put(Constants.MAP_ATT_SABADO, obtenerCasilla(hora, 6));
				fila.put(Constants.MAP_ATT_DOMINGO, obtenerCasilla(hora, 7));
				horario.add(fila);
			}
		}
		editHora = false;
		editCasilla = false;
	}

	/**
	 * Devuelve la casilla correspondiente a la hora y el día que recibe por
	 * parámetros.
	 * 
	 * @param hora
	 *            Hora seleccionada
	 * @param dia
	 *            Día de la semana seleccionado
	 * @return Casilla encontrada o <code>null</code> en caso contrario.
	 */
	private Casilla obtenerCasilla(Hora hora, int dia) {
		Casilla casilla = null;
		Iterator<Casilla> iterator = casillas.iterator();
		while (casilla == null && iterator.hasNext()) {
			Casilla c = iterator.next();
			if (c.getHora().getId() == hora.getId() && c.getDia() == dia) {
				casilla = c;
			}
		}
		return casilla;
	}

	/**
	 * Carga los datos necesarios para mostrar el alta de hora.
	 */
	public void nuevaHora() {
		editHora = true;
		hora = new Hora();
	}

	/**
	 * Carga los datos necesarios para mostrar la pantalla de edición de hora.
	 */
	public void editarHora() {
		editHora = true;
	}

	/**
	 * Carga los datos necesarios para mostrar la pantalla de asignación de
	 * casilla a un grupo determinado.
	 * 
	 * @param dia
	 *            Día de la semana seleccionado
	 */
	public void asignarCasilla(Integer dia) {
		editCasilla = true;
		casilla = new Casilla();
		casilla.setDia(dia);
		casilla.setHora(hora);
		cargarRango(dia);
		grupo = null;
		grupos = horarioService.obtenerGrupos();
	}

	/**
	 * Carga el rango horario seleccionado a partir del atributo
	 * <code>hora</code> y según el día de la semana que recibe por parámetros.
	 * 
	 * @param dia
	 *            Día de la semana seleccionado
	 */
	private void cargarRango(int dia) {
		StringBuffer range = new StringBuffer();
		switch (dia) {
		case 1:
			range.append(Message.get("monday"));
			break;
		case 2:
			range.append(Message.get("tuesday"));
			break;
		case 3:
			range.append(Message.get("wednesday"));
			break;
		case 4:
			range.append(Message.get("thursday"));
			break;
		case 5:
			range.append(Message.get("friday"));
			break;
		case 6:
			range.append(Message.get("saturday"));
			break;
		case 7:
			range.append(Message.get("sunday"));
		}
		range.append(" ");
		range.append(Utils.cadenaRangoHora(hora.getHoraEntrada(), hora.getMinutoEntrada(), hora.getHoraSalida(), hora.getMinutoSalida()));
		rango = range.toString();
	}

	/**
	 * Carga los datos necesarios para mostrar la pantalla de edición de
	 * casilla.
	 */
	public void editarCasilla() {
		grupo = casilla.getGrupo();
		hora = casilla.getHora();
		cargarRango(casilla.getDia());
		editCasilla = true;
		grupos = horarioService.obtenerGrupos();
	}

	/**
	 * Guarda una nueva hora o la modifica si ya existía en la base de datos.
	 */
	public void guardarHora() {
		if (validar()) {
			if (hora.getId() == null) {
				hora.setAlta(new Date());
			}
			horarioService.insertarModificarHora(hora);
			load();
		}
	}

	/**
	 * Da de baja la hora seleccionada.
	 */
	public void eliminarHora() {
		horarioService.cancelarHora(hora);
		load();
	}

	/**
	 * Guarda los cambios de la casilla seleccionada o bien la inserta si no
	 * existía previamente.
	 */
	public void guardarCasilla() {
		Date hoy = new Date();
		// es una alta nueva
		if (casilla.getId() == null) {
			if (validarCasilla()) {
				casilla.setAlta(hoy);
				casilla.setGrupo(grupo);
				horarioService.insertarModificarCasilla(casilla);
				editCasilla = false;
				Message.info("timetable.info.save.success");
				load();
			}
		} else {
			// es una modificación
			if (grupo == null || !grupo.equals(casilla.getGrupo())) {
				casilla.setBaja(hoy);
				horarioService.insertarModificarCasilla(casilla);

				if (grupo != null) {
					int dia = casilla.getDia();
					casilla = new Casilla();
					casilla.setDia(dia);
					casilla.setHora(hora);
					casilla.setAlta(hoy);
					casilla.setGrupo(grupo);
					horarioService.insertarModificarCasilla(casilla);
				}
			}
			editCasilla = false;
			Message.info("timetable.info.save.success");
			load();
		}
	}

	/**
	 * Valida los datos de una casilla para evitar que se realice un cambio si
	 * previamente no se han completado los datos mínimos obligatorios.
	 * 
	 * @return <code>true</code> si la validación es correcta.
	 *         <code>false</code> en caso contrario.
	 */
	private boolean validarCasilla() {
		boolean resultado = true;
		if (grupo == null) {
			resultado = false;
			Message.error("timetable.error.band.null");
		}
		return resultado;
	}

	/**
	 * Valida los datos de una hora para evitar cambios si previamente no se han
	 * completado los datos obligatorios.
	 * 
	 * @return <code>true</code> si la validación es correcta.
	 *         <code>false</code> en caso contrario.
	 */
	private boolean validar() {
		boolean resultado = true;
		if (hora.getHoraEntrada() > hora.getHoraSalida()) {
			Message.error("timetable.error.start");
			resultado = false;
		}
		if (hora.getHoraEntrada() == hora.getHoraSalida() && hora.getMinutoEntrada() >= hora.getMinutoSalida()) {
			Message.error("timetable.error.start");
			resultado = false;
		}
		if (horarioService.obtenerHora(hora.getId(), hora.getHoraEntrada(), hora.getMinutoEntrada(), hora.getHoraSalida(), hora.getMinutoSalida()) != null) {
			Message.error("timetable.error.duplicated");
			resultado = false;
		}
		return resultado;
	}

	/**
	 * Oculta el panel.
	 */
	public void hideSelectBandPanel() {
		editCasilla = false;
	}

	/**
	 * Oculta el panel.
	 */
	public void hideHourPanel() {
		editHora = false;
	}

	public List<Map<String, Object>> getHorario() {
		return horario;
	}

	public void setHorario(List<Map<String, Object>> horario) {
		this.horario = horario;
	}

	public boolean isEditHora() {
		return editHora;
	}

	public void setEditHora(boolean editHora) {
		this.editHora = editHora;
	}

	public Hora getHora() {
		return hora;
	}

	public void setHora(Hora hora) {
		this.hora = hora;
	}

	public Casilla getCasilla() {
		return casilla;
	}

	public void setCasilla(Casilla casilla) {
		this.casilla = casilla;
	}

	public void setGrupo(Grupo grupo) {
		this.grupo = grupo;
	}

	public Grupo getGrupo() {
		return grupo;
	}

	public String getHoraReloj() {
		return horaReloj;
	}

	public void setHoraReloj(String horaReloj) {
		this.horaReloj = horaReloj;
	}

	public String getMinutoReloj() {
		return minutoReloj;
	}

	public void setMinutoReloj(String minutoReloj) {
		this.minutoReloj = minutoReloj;
	}

	public List<SelectItem> getHoras() {
		return horas;
	}

	public void setHoras(List<SelectItem> horas) {
		this.horas = horas;
	}

	public List<SelectItem> getMinutos() {
		return minutos;
	}

	public void setMinutos(List<SelectItem> minutos) {
		this.minutos = minutos;
	}

	public void setGrupos(List<Grupo> grupos) {
		this.grupos = grupos;
	}

	public List<Grupo> getGrupos() {
		return grupos;
	}

	public boolean isEditCasilla() {
		return editCasilla;
	}

	public void setEditCasilla(boolean editCasilla) {
		this.editCasilla = editCasilla;
	}

	public String getRango() {
		return rango;
	}

	public void setRango(String rango) {
		this.rango = rango;
	}
}
